package com.znjc.device.modbus.tcp.slave;

import com.intelligt.modbus.jlibmodbus.Modbus;
import com.intelligt.modbus.jlibmodbus.data.ModbusHoldingRegisters;
import com.intelligt.modbus.jlibmodbus.exception.IllegalDataAddressException;
import com.intelligt.modbus.jlibmodbus.exception.IllegalDataValueException;
import com.intelligt.modbus.jlibmodbus.exception.ModbusIOException;
import com.intelligt.modbus.jlibmodbus.slave.ModbusSlave;
import com.intelligt.modbus.jlibmodbus.slave.ModbusSlaveFactory;
import com.intelligt.modbus.jlibmodbus.tcp.TcpParameters;
import com.znjc.common.constant.DeviceConstants;
import com.znjc.common.core.redis.RedisCache;
import com.znjc.common.utils.StringUtils;
import com.znjc.device.modbus.config.MoudbusTypeConfig;
import com.znjc.device.modbus.holder.MyOwnDataHolder;
import com.znjc.device.modbus.listener.ModbusEventListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.concurrent.Executors;

//springboot启动时创建tcp slave
@Component
public class ModbusSlaveUtil implements CommandLineRunner {
    // 使用日志框架替代System.out
    private static final Logger logger = LoggerFactory.getLogger(ModbusSlaveUtil.class);

    // 依赖注入改为构造器注入（Spring 4.3+ 支持隐式构造器注入）
    @Resource
    RedisCache redisCache;
    @Resource
    MoudbusTcpConfig moudbusTcpConfig;
    @Resource
    MoudbusTypeConfig moudbustypeConfig;

    private ModbusSlave slave;  // 从静态变量改为实例变量

    /**
     * Spring Boot启动完成后执行
     *
     * @param args 命令行参数
     */
    @Override
    public void run(String... args) throws Exception {
        if (1 == moudbustypeConfig.getType()) {
            // 延迟10秒启动，等待其他服务初始化完成
            Thread.sleep(10000);
            logger.info("Starting Modbus TCP slave initialization...");
            // 使用线程池替代直接创建线程
            Executors.newSingleThreadExecutor().submit(this::createSlave);
        }
    }


    /**
     * 创建并启动Modbus从站
     */
    private void createSlave() {
        int retryCount = 3;
        for (int i = 0; i < retryCount; i++) {
            try {
                initSlave();
                createDataHolder();
                logger.info("连接已成功建立。");
                return;
            } catch (Exception e) {
                logger.error("连接尝试 {} 失败: {}", i + 1, e.getMessage());
                if (i == retryCount - 1) {
                    logger.error("TCP 所有连接尝试均失败。正在关闭。。。");
                    closeSlave();
                    break;
                }
                try {
                    Thread.sleep(5000); // 5秒后重试
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                }
            }
        }
    }

    /**
     * 初始化Modbus从站配置
     */
    private void initSlave() throws UnknownHostException, ModbusIOException {
        logger.info("Initializing Modbus connection to {}:{}",
                moudbusTcpConfig.getIp(), moudbusTcpConfig.getPort());
        TcpParameters tcpParameters = new TcpParameters();
        InetAddress address = InetAddress.getByName(moudbusTcpConfig.getIp());
        tcpParameters.setHost(address);
        tcpParameters.setPort(moudbusTcpConfig.getPort());
        tcpParameters.setKeepAlive(true);
        logger.debug("TCP Parameters: {}", tcpParameters);
        // 创建Modbus从站实例
        slave = ModbusSlaveFactory.createModbusSlaveTCP(tcpParameters);
        slave.setServerAddress(moudbusTcpConfig.getSlaveId());
        slave.setReadTimeout(moudbusTcpConfig.getReadTime());
        Modbus.setLogLevel(Modbus.LogLevel.LEVEL_DEBUG);
        logger.debug("TCP parameters initialized: {}", tcpParameters);
    }

    public void createDataHolder() throws ModbusIOException {
        // 创建从机的寄存器
        MyOwnDataHolder dh = new MyOwnDataHolder();
        // 为从机寄存器添加监听事件，这里的监听事件主要是主机如果发送写命令修改从机则控制台输出
        dh.addEventListener(new ModbusEventListener() {
            /**
             * 处理保持寄存器读取请求（功能码03）
             */
            @Override
            public int[] readHoldingRegisterRange(int offset, int quantity) {
                logger.debug("Reading holding registers [offset: {}, quantity: {}]", offset, quantity);
                try {
                    return updateRegisterValues(offset, quantity, RegisterType.HOLDING);
                } catch (Exception e) {
                    logger.error("Error reading holding registers", e);
                    return new int[quantity * 2];  // 返回适当长度的空数据
                }
            }

            /**
             * 处理输入寄存器读取请求（功能码04）
             */
            @Override
            public int[] readInputRegisterRange(int offset, int quantity) {
                logger.debug("Reading input registers [offset: {}, quantity: {}]", offset, quantity);
                try {
                    return updateRegisterValues(offset, quantity, RegisterType.INPUT);
                } catch (Exception e) {
                    logger.error("Error reading input registers", e);
                    return new int[quantity * 2];
                }
            }

            @Override
            public void onWriteToSingleCoil(int address, boolean value) {
                System.out.print("onWriteToSingleCoil: address " + address + ", value " + value);
            }

            @Override
            public void onWriteToMultipleCoils(int address, int quantity, boolean[] values) {
                System.out.print("onWriteToMultipleCoils: address " + address + ", quantity " + quantity);
            }

            @Override
            public void onWriteToSingleHoldingRegister(int address, int value) {
                System.out.print("onWriteToSingleHoldingRegister: address " + address + ", value " + value);
            }

            @Override
            public void onWriteToMultipleHoldingRegisters(int address, int quantity, int[] values) {
                System.out.print("onWriteToMultipleHoldingRegisters: address " + address + ", quantity " + quantity);
            }

        });
        slave.setDataHolder(dh);
        slave.listen();
        logger.info("Data holder initialized and listening");
    }

    /**
     * 关闭从站连接
     */
    private void closeSlave() {
        try {
            if (slave != null && slave.isListening()) {
                slave.shutdown();
                logger.info("Modbus slave shutdown successfully");
            }
        } catch (ModbusIOException e) {
            logger.error("Error shutting down Modbus slave", e);
        }
    }

    /**
     * 更新寄存器值（通用处理）
     *
     * @param offset   寄存器起始地址
     * @param quantity 寄存器数量
     * @param type     寄存器类型
     * @return 寄存器数据数组
     */
    private int[] updateRegisterValues(int offset, int quantity, RegisterType type)
            throws IllegalDataAddressException, IllegalDataValueException {

        if (quantity % 2 != 0) {
            throw new RuntimeException("Quantity must be even for 32-bit float values");
        }

        int floatCount = quantity / 2;
        ModbusHoldingRegisters hr = new ModbusHoldingRegisters(quantity);

        for (int i = 0; i < floatCount; i++) {
            int currentOffset = offset + (i * 2);
            String redisKey = DeviceConstants.DEVICE_VALUE + currentOffset;
            String value = null;
            try {
                value = redisCache.redisTemplate.opsForValue().get(redisKey).toString();
            } catch (Exception e) {
                value = "0";
            }
            float floatValue = parseFloatSafely(value);
            hr.setFloat32At(currentOffset, floatValue);
        }

        if (type == RegisterType.HOLDING) {
            slave.getDataHolder().setHoldingRegisters(hr);
        } else {
            slave.getDataHolder().setInputRegisters(hr);
        }
        return hr.getRegisters();
    }


    /**
     * 安全转换字符串到float
     */
    private float parseFloatSafely(String value) {
        try {
            return StringUtils.isEmpty(value) ? 0.0f : Float.parseFloat(value);
        } catch (NumberFormatException e) {
            logger.warn("Invalid float value: {}", value);
            return 0.0f;
        }
    }

    /**
     * 寄存器类型枚举
     */
    private enum RegisterType {
        HOLDING, INPUT
    }

}
